import 'package:flutter/material.dart';

enum DragDirection {
  left,
  right,
  top,
  bottom,
}

class DragLike extends StatefulWidget {
  final Widget child;
  final bool takeOverControl;//接管控制
  final Widget secondChild;
  final double screenWidth;
  final double outValue;
  final double dragSpeed;
  final Duration? duration;
  final Function(DragDirection dragDirection) onOutComplete;
  final VoidCallback onScaleComplete;
  final ValueChanged<Map> onChangeDragDistance;
  final VoidCallback onPointerUp;
  final VoidCallback? onCancel;
  final DragController? dragController;
  final bool allowDrag;
  final double horScrollMax; //水平滑动最大值，用来修改透明度和滑出
  final double verScrollMax; //
  final Function(DragDirection direction, double percent)? panUpdate;

  DragLike({
    Key? key,
    required this.child,
    this.takeOverControl = false,
    required this.secondChild,
    required this.screenWidth,
    required this.outValue,
    required this.dragSpeed,
    required this.onOutComplete,
    required this.onScaleComplete,
    required this.onChangeDragDistance,
    required this.onPointerUp,
    this.onCancel,
    this.dragController,
    this.duration,
    this.allowDrag = true,
    required this.horScrollMax,
    required this.verScrollMax,
    this.panUpdate,
  })
      : assert(outValue <= 1 && outValue > 0),
        assert(dragSpeed > 0),
        super(key: key);

  @override
  DragLikeState createState() => DragLikeState();
}

class DragLikeState extends State<DragLike> with TickerProviderStateMixin {
  // 滑动不到指定位置返回时的动画
  Animation? animation;

  //位置回去动画
  Animation? positionAnimation;

  // 第二个页面动画到前面
  Animation? scaleAnimation;

  // 按下时的X坐标，以此判断是左滑还是右滑
  double onPressDx = 0;

  // 拖拽时，上一瞬间x轴的位置
  double lastPositionX = 0;

  // 屏幕宽度
  double get screenWidth => widget.screenWidth;

  // 旋转角度
  // double angle = 0;
  final ValueNotifier<double> rotateNotifier = ValueNotifier(0);

  ////add by snjj
  final ValueNotifier<Offset> positionNotifier = ValueNotifier(Offset.zero);

  // 旋转时，x轴的偏移量
  double offsetX = 0;

  // 拖拽时，两个瞬间之间，x轴的差值
  double distanceBetweenTwo = 0;

  // 拖拽发生的时间
  DateTime dragTime = DateTime(0);

  // 第二层的缩放值，0-0.1，因为已设置初始值为0.9
  final ValueNotifier<double> secondScale = ValueNotifier(0);

  // 拖拽距离比0.0-1
  double dragDistance = 0;

  // 滑动流畅值，默认3.8，越小越快
  double dragGvalue = 5;

  // 第二层缩放速度，默认4，越小越快
  double secondScaleSd = 2.3;

  // 动画是否执行完成，执行完成后，控制器执行滑动才有效
  bool scaleComplete = true;

  bool get allowDrag => widget.allowDrag;

  ///开始拖拽的位置
  Offset? touchBeginOffset;

  ///方向
  DragDirection? dragDirection;

  ///拖动比例
  double? dragPercent;

  GestureDetector? ges;

  @override
  void initState() {
    super.initState();
    widget.dragController?.controlerInit(this);
  }

  onPanDown(DragDownDetails value) {
    if (ges == null) {
      return;
    }
    ges!.onPanDown!(value);
  }

  onPanUpdate(DragUpdateDetails value) {
    if (ges == null) {
      return;
    }
    ges!.onPanUpdate!(value);
  }

  onPanEnd(DragEndDetails value) {
    if (ges == null) {
      return;
    }
    ges!.onPanEnd!(value);
  }

  // 更新偏移和缩放量
  void updatePosition(positionX, {iscontroller = false}) {
    // print("positionX = " + positionX.toString());
    // print("lastPositionX = " + lastPositionX.toString());
    double offset = positionX - onPressDx;
    // print("offset = " + offset.toString());

    dragTime = DateTime.now();

    distanceBetweenTwo = positionX - lastPositionX;
    lastPositionX = positionX;

    double offsetAbs = offset.abs();
    // print("offsetAbs = " + offsetAbs.toString());
    double angleTemp =
    double.parse((offset / screenWidth / dragGvalue).toStringAsFixed(3));
    if (rotateNotifier.value != angleTemp) {
      rotateNotifier.value = angleTemp;
      secondScale.value =
          (offsetAbs / screenWidth / dragGvalue) / secondScaleSd;
      if (secondScale.value >= 0.1) secondScale.value = 0.1;
      dragDistance = offsetAbs / screenWidth;
      // print("dragDistance = " + dragDistance.toString());

      if (offset < 0) {
        offsetX = -80;
      } else {
        offsetX = 80;
      }

      double distance = offset / screenWidth;
      double distanceProgress = distance / widget.outValue;
      if (!iscontroller) {
        widget.onChangeDragDistance({
          'distance': distance,
          'distanceProgress': distanceProgress.abs(),
        });
      }
    }
  }

  // 上层以及第二层返回动画执行
  runBackAnimate() {
    AnimationController controller;
    controller = AnimationController(
        duration: const Duration(milliseconds: 200), vsync: this);
    this.animation =
    Tween<double>(begin: rotateNotifier.value, end: 0).animate(controller)
      ..addListener(() {
        rotateNotifier.value = this.animation!.value;
        secondScale.value = rotateNotifier.value.abs() / secondScaleSd;

        if (secondScale.value <= 0) secondScale.value = 0;

        dragDistance = 0;
        if (controller.isCompleted) {
          controller.dispose();
        }
        // setState(() {});
      });
    controller.forward(from: 0);

    runBackAnimatePosition();
  }

  runBackAnimatePosition() {
    scaleComplete = false;
    AnimationController controller;
    controller = AnimationController(
        duration: const Duration(milliseconds: 200), vsync: this);
    this.positionAnimation =
    Tween<Offset>(begin: positionNotifier.value, end: Offset.zero)
        .animate(controller)
      ..addListener(() {
        positionNotifier.value = this.positionAnimation!.value;
        if (controller.isCompleted) {
          controller.dispose();
        }
      });
    controller.forward(from: 0);
  }

  runBottomAnimatePosition({iscontroller = false, String completeTag = ''}) {
    scaleComplete = false;
    // 回调给上层
    widget.onOutComplete(DragDirection.bottom);

    AnimationController controller;
    controller = AnimationController(
        duration: widget.duration == null
            ? const Duration(milliseconds: 250)
            : widget.duration,
        vsync: this);
    // 如果是控制器直接控制滑动，旋转角度直接从0开始
    if (iscontroller) rotateNotifier.value = 0;

    this.positionAnimation =
    Tween<double>(begin: positionNotifier.value.dy, end: 1000).animate(
        controller)
      ..addListener(() {
        positionNotifier.value =
            Offset(positionNotifier.value.dx, this.positionAnimation!.value);
        dragDistance = 0;
        if (controller.isCompleted) {
          //add by snjj
          positionNotifier.value = Offset.zero;
          controller.dispose();
        }
      });

    controller.forward(from: 0);
    if (iscontroller) {
      secondScale.value = 0.0;
    }
  }

  void runOutAnimate(int type,
      {iscontroller = false, String completeTag = ''}) {
    scaleComplete = false;
    // 回调给上层
    widget.onOutComplete(type == -1 ? DragDirection.left : DragDirection.right);

    AnimationController controller;
    controller = AnimationController(
        duration: widget.duration == null
            ? const Duration(milliseconds: 250)
            : widget.duration,
        vsync: this);
    // 如果是控制器直接控制滑动，旋转角度直接从0开始
    if (iscontroller) rotateNotifier.value = 0;
    this.animation =
    Tween<double>(begin: rotateNotifier.value, end: type == 1 ? 0.5 : -0.5)
        .animate(controller)
      ..addListener(() {
        rotateNotifier.value = this.animation!.value;
        dragDistance = 0;
        if (controller.isCompleted) {
          //add by snjj
          positionNotifier.value = Offset.zero;
          controller.dispose();
        }
      });

    controller.forward(from: 0);
    if (iscontroller) {
      secondScale.value = 0.0;
    }
  }

  void runInScaleAnimate() async {
    AnimationController controller;
    controller = AnimationController(
        duration: widget.duration == null
            ? const Duration(milliseconds: 250)
            : widget.duration,
        vsync: this);
    this.scaleAnimation =
    Tween<double>(begin: secondScale.value, end: 0.1).animate(controller)
      ..addListener(() async {
        secondScale.value = this.scaleAnimation!.value;
        if (controller.isCompleted) {
          widget.onScaleComplete();
          scaleComplete = true;
          controller.dispose();
          // 缩放完成以后，将上一层的widget还原到起始位置，不要动画，业务方需要将上层widget的数据替换
          rotateNotifier.value = 0;
        }
        // setState(() {});
      });
    controller.forward(from: 0);
  }

  void cancel() {
    if (widget.onCancel != null) {
      widget.onCancel!();
    }
  }

  @override
  void dispose() {
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {

    if (widget.takeOverControl == true) {
      ges ??= GestureDetector(
        onPanDown: (DragDownDetails value) {
          dragDirection = null;
          dragPercent = null;

          /// 开始拖拽
          if (widget.allowDrag) {
            touchBeginOffset = value.globalPosition;
            lastPositionX = onPressDx = value.globalPosition.dx;
          }
        },
        onPanUpdate: (DragUpdateDetails value) {
          /// 拖拽移动时
          if (widget.allowDrag) {
            updatePosition(value.globalPosition.dx);
            if (touchBeginOffset != null) {
              positionNotifier.value = Offset(
                  (value.globalPosition.dx - (touchBeginOffset?.dx ?? 0)) *
                      0.5,
                  (value.globalPosition.dy - (touchBeginOffset?.dy ?? 0)) *
                      1.12);
            }

            //告诉业务层，移动时的百分比和方向
            if (widget.panUpdate != null) {
              DragDirection dragDir = DragDirection.top;
              double horPercent = 0.0;
              double verPercent = 0.0;

              double beginDx = (touchBeginOffset?.dx ?? 0);
              double beginDy = (touchBeginOffset?.dy ?? 0);

              ///计算水平比例
              horPercent = (beginDx - value.globalPosition.dx).abs() /
                  widget.horScrollMax;

              ///计算垂直比例
              verPercent = (beginDy - value.globalPosition.dy) /
                  widget.verScrollMax;

              ///计算方向
              if (beginDx - value.globalPosition.dx >= 0) {
                //往左滑
                dragDir = DragDirection.left;
              } else {
                //往右滑
                dragDir = DragDirection.right;
              }
              dragPercent = horPercent;

              if (horPercent < 0.2) {
                if (verPercent < -0.2) {
                  dragDir = DragDirection.bottom;
                  dragPercent = verPercent;
                } else {
                  dragDir = DragDirection.top;
                  dragPercent = verPercent;
                }
              }


              dragDirection = dragDir;

              ///通知业务层
              widget.panUpdate!(dragDir, dragPercent ?? 0);
            }
          }
        },
        onPanEnd: (DragEndDetails details) {
          /// 拖拽结束时
          touchBeginOffset = null;
          if (widget.allowDrag) {
            var dragSpeed = details.velocity.pixelsPerSecond.dx.abs();
            if (dragDistance > widget.outValue ||
                dragSpeed >= widget.dragSpeed) {
              if (distanceBetweenTwo > 5) {
                /// 右滑出界
                onRightOut();
                return;
              } else if (distanceBetweenTwo < -5) {
                /// 左滑出界
                onLeftOut();
                return;
              }
            }
          }

          if (dragDirection == DragDirection.left &&
              (dragPercent ?? 0) >= 0.999) {
            /// 左滑出界
            onLeftOut();
            return;
          }
          if (dragDirection == DragDirection.right &&
              (dragPercent ?? 0) >= 0.999) {
            /// 右滑出界
            onRightOut();
            return;
          }

          if (dragDirection == DragDirection.bottom &&
              (dragPercent ?? 0) < -0.999) {
            /// 下滑出界
            onBottomOut();
            return;
          }

          ///其他，回到中心
          onGoCenter();
        },
        child: ValueListenableBuilder<double>(
          valueListenable: rotateNotifier,
          builder:
              (BuildContext context, double rotationValue, Widget? child) {
            return Transform.rotate(
              angle: rotationValue,
              origin: Offset(rotationValue + offsetX, 1500),
              alignment:
              Alignment.lerp(Alignment.center, Alignment.bottomCenter, 1),
              child: ValueListenableBuilder<Offset>(
                valueListenable: positionNotifier,
                builder: (BuildContext context, Offset positionValue,
                    Widget? child) {
                  return Transform.translate(
                    offset: positionValue,
                    child: child,
                  );
                },
                child: widget.child,
              ),
            );
          },
        ),
      );

      return Container(
          child: Stack(
            children: [
              ValueListenableBuilder(
                valueListenable: secondScale,
                builder: (BuildContext context, double value, Widget? child) {
                  return Transform.scale(
                    scale: 0.9 + secondScale.value,
                    child: child,
                  );
                },
                child: widget.secondChild,
              ),
              ges!,
            ],
          ));
    }  else {

      return Container(
          child: Stack(
            children: [
              ValueListenableBuilder(
                valueListenable: secondScale,
                builder: (BuildContext context, double value, Widget? child) {
                  return Transform.scale(
                    scale: 0.9 + secondScale.value,
                    child: child,
                  );
                },
                child: widget.secondChild,
              ),
              GestureDetector(
                onPanDown: (DragDownDetails value) {
                  dragDirection = null;
                  dragPercent = null;

                  /// 开始拖拽
                  if (widget.allowDrag) {
                    touchBeginOffset = value.globalPosition;
                    lastPositionX = onPressDx = value.globalPosition.dx;
                  }
                },
                onPanUpdate: (DragUpdateDetails value) {
                  /// 拖拽移动时
                  if (widget.allowDrag) {
                    updatePosition(value.globalPosition.dx);
                    if (touchBeginOffset != null) {
                      positionNotifier.value = Offset(
                          (value.globalPosition.dx - (touchBeginOffset?.dx ?? 0)) *
                              0.5,
                          (value.globalPosition.dy - (touchBeginOffset?.dy ?? 0)) *
                              1.12);
                    }

                    //告诉业务层，移动时的百分比和方向
                    if (widget.panUpdate != null) {
                      DragDirection dragDir = DragDirection.top;
                      double horPercent = 0.0;
                      double verPercent = 0.0;

                      double beginDx = (touchBeginOffset?.dx ?? 0);
                      double beginDy = (touchBeginOffset?.dy ?? 0);

                      ///计算水平比例
                      horPercent = (beginDx - value.globalPosition.dx).abs() /
                          widget.horScrollMax;

                      ///计算垂直比例
                      verPercent = (beginDy - value.globalPosition.dy) /
                          widget.verScrollMax;

                      ///计算方向
                      if (beginDx - value.globalPosition.dx >= 0) {
                        //往左滑
                        dragDir = DragDirection.left;
                      } else {
                        //往右滑
                        dragDir = DragDirection.right;
                      }
                      dragPercent = horPercent;

                      if (horPercent < 0.2) {
                        if (verPercent < -0.2) {
                          dragDir = DragDirection.bottom;
                          dragPercent = verPercent;
                        }  else {
                          dragDir = DragDirection.top;
                          dragPercent = verPercent;
                        }
                      }


                      dragDirection = dragDir;
                      ///通知业务层
                      widget.panUpdate!(dragDir, dragPercent??0);
                    }
                  }
                },
                onPanEnd: (DragEndDetails details) {
                  /// 拖拽结束时
                  touchBeginOffset = null;
                  if (widget.allowDrag) {

                    var dragSpeed = details.velocity.pixelsPerSecond.dx.abs();
                    if (dragDistance > widget.outValue ||
                        dragSpeed >= widget.dragSpeed) {
                      if (distanceBetweenTwo > 5) {
                        /// 右滑出界
                        onRightOut();
                        return;
                      } else if (distanceBetweenTwo < -5) {
                        /// 左滑出界
                        onLeftOut();
                        return;
                      }
                    }
                  }

                  if (dragDirection == DragDirection.left &&
                      (dragPercent ?? 0) >= 0.999) {
                    /// 左滑出界
                    onLeftOut();
                    return;
                  }
                  if (dragDirection == DragDirection.right &&
                      (dragPercent ?? 0) >= 0.999) {
                    /// 右滑出界
                    onRightOut();
                    return;
                  }

                  if (dragDirection == DragDirection.bottom && (dragPercent ?? 0) < -0.999) {
                    /// 下滑出界
                    onBottomOut();
                    return;
                  }

                  ///其他，回到中心
                  onGoCenter();
                },
                child: ValueListenableBuilder<double>(
                  valueListenable: rotateNotifier,
                  builder:
                      (BuildContext context, double rotationValue, Widget? child) {
                    return Transform.rotate(
                      angle: rotationValue,
                      origin: Offset(rotationValue + offsetX, 1500),
                      alignment:
                      Alignment.lerp(Alignment.center, Alignment.bottomCenter, 1),
                      child: ValueListenableBuilder<Offset>(
                        valueListenable: positionNotifier,
                        builder: (BuildContext context, Offset positionValue,
                            Widget? child) {
                          return Transform.translate(
                            offset: positionValue,
                            child: child,
                          );
                        },
                        child: widget.child,
                      ),
                    );
                  },
                ),
              )
            ],
          ));


    }


  }

  onGoCenter() {
    runBackAnimate();
    // 取消回调
    if (widget.onCancel != null) {
      cancel();
    }
  }

  onLeftOut() {
    /// 左滑出界
    runOutAnimate(-1);
    runInScaleAnimate();
    widget.onPointerUp();
  }

  onRightOut() {
    /// 右滑出界
    runOutAnimate(1);
    runInScaleAnimate();
    widget.onPointerUp();
  }

  onBottomOut() {
    /// 底部出界
    runBottomAnimatePosition();
    runInScaleAnimate();
    widget.onPointerUp();
  }
}

class DragController {
  DragLikeState? _widgetState;

  void controlerInit(DragLikeState _widgetState) {
    this._widgetState = _widgetState;
  }

  // 通过控制器左滑出以后，默认回调给上层left，这里可以根据业务自定义把left修改为custom_left等等
  void toLeft({String completeTag = ''}) {
    if (_widgetState!.scaleComplete && _widgetState!.allowDrag) {
      _widgetState!.updatePosition(0.0, iscontroller: true);
      _widgetState!
          .runOutAnimate(-1, iscontroller: true, completeTag: completeTag);
      _widgetState!.runInScaleAnimate();
    }
    if (!_widgetState!.allowDrag) {
      _widgetState!.cancel();
    }
  }

  // 通过控制器右滑出以后，默认回调给上层right，这里可以根据业务自定义把right修改为custom_right等等
  void toRight({String completeTag = ''}) {
    if (_widgetState!.scaleComplete && _widgetState!.allowDrag) {
      _widgetState!.updatePosition(0.0, iscontroller: true);
      _widgetState!
          .runOutAnimate(1, iscontroller: true, completeTag: completeTag);
      _widgetState!.runInScaleAnimate();
    }
    if (!_widgetState!.allowDrag) {
      _widgetState!.cancel();
    }
  }
}
